(*Moteur 3D affichant un fichier ".obj" passé en paramètre *)

(*Variables*)
let dim = ref (1000, 800)
let fos = float_of_string
let ios = int_of_string
let foi = float_of_int
let iof = int_of_float
let split = Str.split(Str.regexp_string " ")
let strip = ref false
let triangles = ref true

(*Variables des contrôles*)

let fkey = ref false
let qkey = ref false
let zkey = ref false
let dkey = ref false
let skey = ref false
let lkey = ref false
let pkey = ref false
let akey = ref false
let ekey = ref false
let zoom = ref false
let dezoom = ref false
let forward = ref false
let left = ref false
let right = ref false
let back = ref false
let up = ref false
let down = ref false
let esc = ref false

(*Variable associée à la liste de couleurs*)

let dictionnaire = [((0.,0.,255.),0.);((0.,63.,0.),10.);((0.,127.,0.),25.);((0.,191.,0.),40.);((0.,255.,0.),65.);((0.,255.,127.),80.);((127.,255.,127.),100.);((191.,255.,191.),120.)]

(*Variable de l'échelle*)

let ech = ref 200.



(*Lecture de l'OBJ*)
let rec read vl fl ch =
  match try Some(split (input_line ch)) with _ -> None with
    | None -> vl, fl
    | Some["v";x;y;z] -> read ((fos x, fos y, fos z) :: vl) fl ch
    | Some["f";i;j;k] -> read vl ((fos i, fos j, fos k) :: fl) ch
    | Some s -> read vl fl ch

let vfl fl vl =
  let max = Array.length(fl) in
  let rec t result fl vl x = match x with
    | x when (x>max-1) -> result
    | x -> let (a,b,c) = fl.(x) in
        t ((vl.((iof a)-1),vl.((iof b)-1),vl.((iof c)-1))::result) fl
	  vl (x+1)
  in t [] fl vl 0

let load_obj () =
  let vertices, faces =
    let ch = open_in Sys.argv.(1) in
    let vl, fl = read [] [] ch in
      close_in ch;
    Printf.printf "%d vertices, %d triangles\n%!" (List.length vl) (List.length fl);
      Array.of_list (List.rev vl), Array.of_list (List.rev fl) 
  in let result2 = vfl faces vertices in
    Array.of_list (List.rev result2)

(*Gestion couleurs*)

let rec getColors (value,dictionnaire) = match (value,dictionnaire) with
  | (_,[]) -> (1., 1., 1.)
  | (valeur,(c,v)::dico) when v = valeur -> c
  | (valeur,(c,v)::dico) -> getColors(valeur,dico)

(*Affichage*)

let display result3 ()  =
  GlClear.clear [`color; `depth];
  Gl.enable `depth_test;
 
  if(!strip) then (*GlDraw.begins `line_strip;*) (*c'est caca*)
    GlDraw.polygon_mode `both `line;(*line ou point ou fill*)
  if(!triangles) then 
    GlDraw.begins `triangles;


(*Affichage 3D*)
let l = Array.length(result3) in
let ((aa,bb,cc),(dd,ee,ff),(gg,hh,ii)) = result3.(l-1) in
let middle = aa/.(!ech)/.2. in
	  for i = 0 to l-1 do                                          (*récupérer les couleurs dans getColor*)
	    let ((a,b,c),(d,e,f),(g,h,i)) = result3.(i) in
	      GlDraw.color (getColors (c,dictionnaire));
              GlDraw.vertex3(a/.(!ech)-.middle,b/.(!ech)-.middle,c/.(!ech)-.middle);
	      GlDraw.color (getColors (f,dictionnaire));
	      GlDraw.vertex3(d/.(!ech)-.middle,e/.(!ech)-.middle,f/.(!ech)-.middle);
	      GlDraw.color (getColors (i,dictionnaire));
	      GlDraw.vertex3(g/.(!ech)-.middle,h/.(!ech)-.middle,i/.(!ech)-.middle);
	  done;
	  GlDraw.ends ();

    Gl.disable `depth_test;
    Gl.flush ()

(*Changement d'affichage et manipulations*)

(* Listing des contrôles:
- z, s, q, d, a, e pour les rotations
- 8, 4, 2, 6 du pavé numérique pour les translations
- 7, 1 du pavé numérique pour les translations de hauteur
- +, - du pavé numérique pour zoom et dezoom
- f, l, p pour les 3 modes d'affichages*)

let idle () =
  if(!qkey) then
    begin 
      Glut.postRedisplay ();
      GlMat.rotate3 (1.) (0., 1., 0.);
      qkey:=false;
    end
  else
    if(!dkey) then
      begin 
	Glut.postRedisplay ();
	GlMat.rotate3 (-1.) (0., 1., 0.);
	dkey:=false;
      end
      else
      if(!zkey) then
	begin
	  Glut.postRedisplay ();
	  GlMat.rotate3 (1.) (1., 0., 0.);
	  zkey:=false;
	end
      else
	if(!skey) then
	  begin
	    Glut.postRedisplay ();
	    GlMat.rotate3 (-1.) (1., 0., 0.);
	    skey:=false;
	  end
	else
	  if(!akey) then
	    begin
	      Glut.postRedisplay ();
	      GlMat.rotate3 (-1.) (0., 0., 1.);
	      akey:=false;
	    end
	  else
	    if(!ekey) then
	      begin
		Glut.postRedisplay ();
		GlMat.rotate3 (1.) (0., 0., 1.);
		ekey:=false;
	      end
	    else
	      if(!zoom) then
		begin
		  Glut.postRedisplay ();
		  ech:= !ech -. 10.;
		  zoom:=false;
		end
	      else
		if(!dezoom) then
		  begin
		    Glut.postRedisplay ();
		    ech:= !ech +. 10.;
		    dezoom:=false;
		  end
		else
		  if(!pkey) then
		    begin
		      Glut.postRedisplay ();
		      GlDraw.polygon_mode `both `point;
		      pkey:=false;
		    end
		  else
		    if(!lkey) then
		      begin
			Glut.postRedisplay ();
			GlDraw.polygon_mode `both `line;
			lkey:=false;
		      end
		    else
		      if(!fkey) then
			begin
			  Glut.postRedisplay ();
			  GlDraw.polygon_mode `both `fill;
			  fkey:=false;
			end
		      else
			if(!forward) then
			  begin
			    Glut.postRedisplay ();
			    GlMat.translate ~x:0. ~y:0.05 ~z:0. ();
			    forward:=false;
			  end
			else
			  if(!back) then
			    begin
			      Glut.postRedisplay ();
			      GlMat.translate ~x:0. ~y:(-0.05) ~z:0. ();
			      back:=false;
			    end
			  else
			    if(!left) then
			      begin
				Glut.postRedisplay ();
				GlMat.translate ~x:(-0.05) ~y:0. ~z:0. ();
				left:=false;
			      end
			    else
			      if(!right) then
				begin
				  Glut.postRedisplay ();
				  GlMat.translate ~x:0.05 ~y:0. ~z:0. ();
				  right:=false;
				end
			      else
				if(!up) then
				  begin
				    Glut.postRedisplay ();
				    GlMat.translate ~x:0. ~y:0. ~z:0.05 ();
				    up:=false;
				  end
				else
				  if (!down) then
				    begin
				      Glut.postRedisplay ();
				      GlMat.translate ~x:0. ~y:0. ~z:(-0.05) ();
				  down:=false;
				end

		  
(*Propriétés de l'affichage*)
let reshape ~w ~h =
  let w = max 1 w and h = max 1 h in
    dim := (w, h);
    GlClear.clear [`color; `depth];
    GlMat.mode `projection;
    GlMat.load_identity ();
    GluMat.perspective ~fovy:20.0 ~aspect:(1.0 *. float w /. float h)
      ~z:(0.1,100.0);
    begin
      GluMat.look_at ~eye:(0., -10., 5.) ~center:(0., 0., 0.) ~up:(0.,1.,0.);
    end;
    GlMat.mode `modelview;
    GlMat.load_identity ()
      

(*Le Main*)
let main () =
  let result3 = load_obj () in
  let _ = Glut.init Sys.argv in
    Glut.initWindowSize ~w:(fst !dim) ~h:(snd !dim);
    ignore (Glut.createWindow ~title:"Mapplestore");
    Glut.reshapeFunc ~cb:reshape;
    Glut.displayFunc ~cb:(display result3);
    Glut.idleFunc ~cb:(Some idle);
    Glut.keyboardFunc ~cb:(fun ~key ~x ~y ->
      if key=122 then zkey:=true;
      if key=113 then qkey:=true;
      if key=100 then dkey:=true;
      if key=115 then skey:=true;
      if key=043 then zoom:=true;
      if key=108 then lkey:=true;
      if key=112 then pkey:=true;
      if key=102 then fkey:=true;
      if key=097 then akey:=true;
      if key=101 then ekey:=true;
      if key=056 then forward:=true;
      if key=050 then back:=true;
      if key=052 then left:=true;
      if key=054 then right:=true;
      if key=055 then up:=true;
      if key=049 then down:=true;
      if key=045 then dezoom:=true;
      if key=027 then exit 0);
    Glut.mainLoop()

let _ = main ()
